package com.lzwork.gpt.utility.batchjob.inner;

import java.io.File;
import java.util.Iterator;
import java.util.List;
import java.util.function.Consumer;

import com.lzwork.gpt.utility.batchjob.JobOption;
import com.lzwork.gpt.utility.batchjob.SingleJob;
import com.lzwork.utility.TimeTicker;
import com.lzwork.utility.TimeTicker.TickFormat;
import com.lzwork.utility.data.TypeEnum;
import com.lzwork.utility.file.form.LineData;
import com.lzwork.utility.logger.LOGGER;
import com.lzwork.web.data.BoolResultInfo;

import lombok.Getter;

/**
 * @author LiZheng
 * @date 2024年8月1日 10:32:38
 */

public abstract class InnerJob
{
	public static interface SourceLoader
	{
		List<LineData> loadSource(File source, JobOption option, Consumer<String> logger) throws InterruptedException;
	}

	public static int gcRange = 0;

	public InnerJob(SingleJob owner)
	{
		this.owner = owner;
		this.type = owner.getType();
	}

	protected abstract SourceLoader sourceLoader();

	protected abstract JobParser initParser();

	protected JobParser linkedParser;
	protected JobOption linkedOption;

	protected TypeEnum type;
	protected SingleJob owner;

	protected boolean loaded = false;

	protected int parsed = 0;
	protected int total = 0;
	protected int empty = 0;
	protected File lastTemp = null;

	@Getter
	protected boolean inLoop = false;

	public TypeEnum getType()
	{
		return type;
	}

	protected void reset()
	{
		loaded = false;
		parsed = 0;
		total = 0;
		empty = 0;

		if (linkedParser == null)
			linkedParser = initParser();
	}

	public static BoolResultInfo jobBreakCheck() throws InterruptedException
	{
		Thread t = Thread.currentThread();
		if (t.isInterrupted())
		{
			String info = "[" + t.getId() + "] " + t.getName();
			throw new InterruptedException("Interrupt job " + info + " by user action.");
		}

		return BoolResultInfo.makeFalse("");
	}

	public String progress()
	{
		if (!loaded)
			return "正在读取源文件";

		return parsed + "/" + total;
	}

	public String log(String text, JobParser from)
	{
		if (from == null || from != linkedParser)
			return null;
		return log(text);
	}

	protected String log(String text)
	{
		return log(text, false);
	}

	protected String log(String text, boolean backOnly)
	{
		return log("", text, backOnly);
	}

	protected String log(String tag, String text, boolean backOnly)
	{
		if (!backOnly)
			LOGGER.log(tag, text);
		owner.addLog(text);
		return text;
	}

	public void doJob() throws InterruptedException
	{
		reset();

		log("Begin job");

		TimeTicker ticker = new TimeTicker();

		if (linkedParser instanceof JobParserRemote)
		{
			JobParserRemote jrp = (JobParserRemote) linkedParser;

			BoolResultInfo vs = jrp.validateServer();

			if (!vs.isDone())
			{
				String msg = log(vs.info());
				owner.onJobBreak(msg);
				return;
			}
		}

		JobOption option = owner.getOption();

		List<LineData> allLines = sourceLoader().loadSource(owner.getSource(), option, this::log);

		loaded = true;
		total = allLines.size();
		if (total == 0)
		{
			log("No valid data in source file.");
			owner.onJobBreak("Empty Source File.");
			return;
		}

		dealAllLines(allLines);

		log("Total " + parsed + " lines parsed. Cost: " + ticker.tickS(TickFormat.Auto, true));
		owner.onJobDone();
	}

	protected void dealAllLines(List<? extends LineData> allLines) throws InterruptedException
	{
		beforeLineLoop(allLines);

		inLoop = true;

		Iterator<? extends LineData> lit = allLines.iterator();
		int index = 0;

		while (lit.hasNext())
		{
			jobBreakCheck();
			LineData line = lit.next();

			if (line.hasData())
			{
				parsed++;
				dealSingleLine(line);
			}

			index++;
			lit.remove();

			if (gcRange > 0 && index % gcRange == 0)
				System.gc();
		}

		inLoop = false;

		afterLineLoop();
	}

	protected void beforeLineLoop(List<? extends LineData> allLines)
	{
	}

	protected void afterLineLoop()
	{
	}

	protected abstract List<String> dealSingleLine(LineData line);

	public void stop(boolean force)
	{
		// TODO
	}

	public JobOption getOption()
	{
		linkedOption = createOption();
		return linkedOption;
	}

	protected abstract JobOption createOption();
}
